#ifndef UUID_8093275B12B443D301BA9A86CEDBA965
#define UUID_8093275B12B443D301BA9A86CEDBA965

#include <gvl/sockets/sockets.hpp>
#include <gvl/io/common_bitstream.hpp>
#include <gvl/list.hpp>
#include <utility>
#include <deque>
#include "utility.hpp"
#include "cookie.hpp"
#include "section.hpp"


namespace Psync5
{

struct Channel : NonCopyable
{
	Channel()
	: priority(64)
	, nextSectionId(0)
	{
	}

	SendSection* newSendSection()
	{
		auto section = new SendSection(nextSectionId);
		nextSectionId = (nextSectionId + 1) & 0xffff;
		return section;
	}

	void queue(SendSection* s)
	{
		sendQueue.push_back(s);
	}

	//uint32_t id;
	int priority;
	int nextSectionId;
	gvl::list<SendSection> sendQueue;
	gvl::list<SendSection> resendQueue;

	ReceiveQueue recvQueue;
};

struct Packet;
struct Node;
struct PacketDataWriter;

struct ReliableLayer : NonCopyable
{
	enum State
	{
		Disconnected,     // The initial state
		Initiated,        // First Init packet has been sent, waiting for InitAck
		Cookied,          // Replied with cookie, waiting for CookieAck
		Connected,        // Fully connected
		ShutdownWaitAck,  // Sent shutdown request, waiting for ShutdownAck
		ShutdownWaitComplete, // Sent ack, waiting for ShutdownComplete
	};

	static int const LatencyHistorySize = 64;

	ReliableLayer(Node* nodeInit, gvl::internet_addr const& addrInit, gvl::socket sockInit)
	: node(nodeInit)
	, addr(addrInit)
	, sock(sockInit)
	, nextPacketId(0)
	, prevPacketTime_(0)
	, state(Disconnected)
	, latencyHistoryPos(0)
	, latencyHistoryCount(0)
	{
	}

	void initiate();
	void disconnect();
	void send(Packet const& packet);
	
	gvl::internet_addr const& address() const
	{ return addr; }

	void handleInitAck(Packet packet);
	bool handleCookie();
	bool handleCookieAck(Packet packet);
	void handleShutdown(Packet packet);
	void handleShutdownAck(Packet packet);
	void handleShutdownComplete(Packet packet);
	void process();

	/*
	Channel* channel(int id)
	{
		if(id < 0 || unsigned(id) >= channels.size())
			return 0;
		return channels[id].get();
	}*/

	Channel& channel()
	{ return channels[0]; }
	
	bool connected() const
	{ return state == Connected; }

	bool disconnected() const
	{ return state == Disconnected; }

	bool allowsSending() const
	{ return state != Disconnected && state != ShutdownWaitAck && state != ShutdownWaitComplete; }

	bool allowsReceiving() const
	{ return state == Connected; }

	void assembleAndSendPacket();

	void recv(Packet packet);

	float calcLatency();

	bool isTimeForPacket();

	virtual void flush()
	{}

private:
	void sendInit_();
	void sendCookie_(bool sendData);
	void sendShutdown_();
	void sendShutdownAck_();
	void sendShutdownComplete_();
	bool packetTimedOut_(uint32_t timeOut);
	bool assemblePacket_(Packet& destPacket);
	void resetRecv_();
	void resetSend_();
	void reportAckLatency_(uint32_t ms);
	bool tryAddSections_(PacketDataWriter& writer, bool& anythingToSend);

	Node* node;

	Channel channels[1];
	uint32_t nextPacketId;
	uint32_t prevPacketTime_;
	gvl::internet_addr addr;
	gvl::socket sock;
	State state;

	std::deque<uint32_t> pendingAcks;

	int latencyHistory[LatencyHistorySize];
	uint32_t latencyHistoryPos;
	uint32_t latencyHistoryCount;

	InitCookie recvCookie;
};

struct Link : gvl::list_node<>, ReliableLayer
{
	Link(Node* nodeInit, int idInit, gvl::internet_addr const& addrInit, gvl::socket sockInit)
	: id(idInit)
	, ReliableLayer(nodeInit, addrInit, sockInit)
	{
	}


	int id;
};

}

#endif // UUID_8093275B12B443D301BA9A86CEDBA965
